No non-trivial software program is written the first time without errors. The process of finding and fixing the errors in a software program is known as debugging because such errors are commonly called bugs.
There are four phases in a software development program. In the first phase, the specifications for the software product are determined and the software is designed to meet those specifications. In the second phase, or coding phase, source code for the software is written in a high-level computer language, such as the "C" programming language. The source code is compiled into an executable program, consisting of low-level assembly language instructions.
During the third, or testing, phase, the program is tested by executing the program repeatedly for a variety of inputs. Finally in the fourth phase, errors in the behavior of the program must be diagnosed and fixed. This debugging phase of a software development project poses a great management challenge because it is often difficult to predict how much time and effort it may take to find a particular bug. Accordingly, there is a strong need for good tools to diagnose software bugs.
A common tool for diagnosing software errors is a source code debugger. A source code debugger allows the software developer to step through an execution of a program line-by-line in the source code. While a developer is stepping through a program, the source code debugger stops and starts the execution of the program. When the execution of the program has stopped on a line of the program, the developer can determine information about the state of the execution of the program, such as which subroutine of the program is executing and what values the program's variables are currently holding.
If the developer needs to trace through a section of code in which the source code is not or was never available, then the developer uses an assembly language debugger, which can also be a feature of a source code debugger. An assembly language debugger allows the developer to stop and restart the execution of the program at any machine level instruction and inspect the contents of machine registers and memory locations.
Both source code debuggers and assembly language debuggers are capable of diagnosing a large number of errors, but they are not so useful for finding certain kinds of bugs. In particular, they do not work well for software written for real-time systems.
In a real-time system, the timing of the events occurring in the system is critical. For example, race conditions can be a source of pernicious bugs. A race condition occurs when there are two signals occurring asynchronously around the same time in which the behavior of the program is dependent on the order in which those signals occur. However, a source level debugger or an assembly level debugger operates by stopping the execution of a program. As a result, the race condition may disappear because one of the components producing a signal is delayed by the debugger so that one signal always occurs after the other signal.
One approach to debugging race conditions is to put a probe of a logic analyzer or oscilloscope on a hardware component that carries the signal under consideration. In this manner, the developer can determine the exact timing of the signals without affecting the timing characteristics of the program, which is not feasible during a session with a source code or assembly language debugger.
A drawback to the use of logic analyzers and oscilloscopes is that they provide information only about the operating characteristics of the computer hardware, not the software. For many bugs, it is useful to know the current state of the execution of the program when signals occur. As a result, programmers embed statements in their software to output state information, usually in the form of text strings to an existing port of the computer system for transmission to a monitoring device, such as a diagnostic computer.
However, there are a number of disadvantages in using an existing port for outputting debugging information. As one example, the existing port was originally intended to provide some functionality to the computer. It may be difficult, if not impossible, to share that port for its original purpose and for debugging.
Another disadvantage with using an existing port is that the use is intrusive. This is because the port must output the debugging data in real time, stealing processing time away from the computer system. Typically, to use an existing port, the debugging information is queued to a device driver. The device driver operates under interrupt control and thus will temporarily stop the execution of the program to output the queued debugging information. However, even temporarily stopping the execution can sufficiently perturb the timing characteristics of the program to make diagnosing a race condition impossible.
Furthermore, an existing port usually operates more slowly than the main processor executing the computer program being debugged. Thus, the device driver for the port needs to store debugging information in a buffer, reducing the amount of available memory for the program. Some programs have no memory left to spare.
Therefore, there is a need for a non-intrusive diagnostic tool that can provide information about the operating characteristics of the computer software.